package com.authine.cloudpivot.ext.applicationservice;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.cola.dto.RequestContext;
import com.alibaba.cola.dto.SingleResponse;
import com.authine.cloudpivot.ext.Enum.*;
import com.authine.cloudpivot.ext.constants.AppConst;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.constants.PlatformConst;
import com.authine.cloudpivot.ext.dto.GatewayParam;
import com.authine.cloudpivot.ext.dto.GatewayResponse;
import com.authine.cloudpivot.ext.dto.form.InvoiceApplyForm;
import com.authine.cloudpivot.ext.dto.model.invoice.*;
import com.authine.cloudpivot.ext.dto.model.settle.SettleInvoice;
import com.authine.cloudpivot.ext.dto.model.settle.SettleInvoiceDetail;
import com.authine.cloudpivot.ext.dto.model.trade.Trade;
import com.authine.cloudpivot.ext.dto.person.MemberBankAccountDto;
import com.authine.cloudpivot.ext.dto.query.IdType;
import com.authine.cloudpivot.ext.dto.query.SettleApplyQuery;
import com.authine.cloudpivot.ext.dto.system.OrganizationDto;
import com.authine.cloudpivot.ext.service.invoice.InvoiceApplyService;
import com.authine.cloudpivot.ext.service.invoice.impl.InvoiceApplyServiceImpl;
import com.authine.cloudpivot.ext.service.organiztion.OrganizationService;
import com.authine.cloudpivot.ext.service.settle.SettleService;
import com.authine.cloudpivot.ext.service.settle.impl.SettleServiceImpl;
import com.authine.cloudpivot.ext.utils.*;
import com.authine.cloudpivot.file.res.AttachmentRes;
import com.authine.cloudpivot.file.res.UploadImageRes;
import com.authine.hermes.app.file.feign.FtpFeignServiceI;
import com.authine.hermes.app.launcher.service.ApplicationService;
import com.authine.mvp.app.launcher.domain.AttachmentFormat;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Maps;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.ws.rs.POST;
import javax.ws.rs.Path;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.stream.Collectors;

import static com.authine.hermes.app.launcher.service.ApplicationServiceFoundation.CUSTOM_SERVICE;

/**
 * swagger 财务开放接口
 */
@Path("FinanceOpenInterfaceController")
@Slf4j
public class FinanceOpenInterfaceController extends ApplicationService {
    private static final FtpFeignServiceI ftpFeignServiceI = ApplicationContextUtils.getBean(FtpFeignServiceI.class);
    TransactionTemplate transactionTemplate = ApplicationContextUtils.getBean(TransactionTemplate.class);
    public static InvoiceApplyService invoiceApplyService = new InvoiceApplyServiceImpl();
    public static OrganizationService organizationService = new OrganizationService();
    public static SettleService settleService = new SettleServiceImpl();

    @Path("saleApplyInvoice")
    @POST
    @Operation(summary = "外部开放接口-客户申请开票",description = "",tags = CUSTOM_SERVICE)
    public GatewayResponse saleApplyInvoice(GatewayParam gatewayParam){
        log.info("方法开始:执行类：{},执行方法：customerInvoiceApply（【客户】客户申请开票）,请求人{}" + this.getClass().getName(), RequestContext.getCorpLoginId());
        FinanceInvoiceApplyVO financeInvoiceApplyVO = BeanUtil.mapToBean(gatewayParam.getBody(), FinanceInvoiceApplyVO.class, true);
        GatewayResponse checkParamResponse = this.checkParam(financeInvoiceApplyVO);
        if(checkParamResponse.getCode() == -1){
            return checkParamResponse;
        }
        IdType idType = IdType.builder().businessType(InvoiceBusinessType.Product).direction(TradeDirection.Sale).build();
        if(!CollectionUtils.isEmpty(financeInvoiceApplyVO.getCustomerOrderIds())){
            //通过订单维度申请开票
            idType.setWays(OriginIDType.CustomerOrder).setIds(financeInvoiceApplyVO.getCustomerOrderIds().stream().map(String::valueOf).collect(Collectors.toList()));
        }else{
            List<Trade> tradeList = JdbcTemplateUtils.builder(ModelConst.T_TRADE).selectField("*")
                    .in("order_goods_id", financeInvoiceApplyVO.getOrderGoodsIds())
                    .eq("direction", TradeDirection.Sale.name())
                    .queryForPOJOList(Trade.class);
            idType.setWays(OriginIDType.Trade).setIds(tradeList.stream().map(t->t.getId()).filter(StringUtils::isNotEmpty).collect(Collectors.toList()));
        }
        List<ApplyInvoiceDetailDto>  canApplyInvoiceList = invoiceApplyService.viewCanInvoice(idType);
        if (CollectionUtils.isEmpty(canApplyInvoiceList)) {
            return GatewayResponse.failed("未获取到可申请信息");
        }
        List<String> organiztionIds = canApplyInvoiceList.stream().map(t->t.getOrganization_id().getId()).collect(Collectors.toList());
        if(organiztionIds ==null || organiztionIds.size()!=1){
            GatewayResponse.failed("申请组织获取异常");
        }
        OrganizationDto transactionOrganiztion = organizationService.getTransactionOrganiztionId(organiztionIds.get(0));
        if(ObjectUtils.isEmpty(transactionOrganiztion)){
            return GatewayResponse.failed("结算组织获取异常");
        }
        InvoiceType type;
        Integer invoiceType = financeInvoiceApplyVO.getInvoiceType();
        if (invoiceType == 1) {
            type = InvoiceType.VatOrdinary;
        } else if (invoiceType == 2) {
            type = InvoiceType.VatSpecial;
        } else if (invoiceType == 3) {
            type = InvoiceType.NoInvoicing;
        } else if (invoiceType == 4) {
            type = InvoiceType.IndividualInvoicing;
        } else if (invoiceType == 5) {
            type = InvoiceType.VatOrdinaryElectronic;
        } else {
            return GatewayResponse.failed("开票类型不正确");
        }
        InvoiceExtendsAttr invoiceExtendsAttr = null;
        if (RegexUtils.notNull(financeInvoiceApplyVO.getExtendsAttr())) {
            //赋值泛微传过来的值到扩展属性中
            invoiceExtendsAttr = InvoiceExtendsAttr.builder().thfwPunchoutValue(financeInvoiceApplyVO.getExtendsAttr()).build();
        }
        InvoiceApplyForm invoiceApplyForm = InvoiceApplyForm.builder()
                .type(type.name())
                .direction(TradeDirection.Sale.name())
                .invoiceCreationWays(OriginIDType.Trade)
                .purchase_name(financeInvoiceApplyVO.getInvoiceName())
                .purchase_number(financeInvoiceApplyVO.getInvoiceTaxNumber())
                .purchase_address(financeInvoiceApplyVO.getInvoiceAddress())
                .purchase_phone(financeInvoiceApplyVO.getInvoicePhone())
                .purchase_bank(financeInvoiceApplyVO.getInvoiceBank())
                .purchase_account(financeInvoiceApplyVO.getInvoiceAccount())
                .receiver(financeInvoiceApplyVO.getReceiverName())
                .phone(financeInvoiceApplyVO.getReceiverMobile())
                .address(financeInvoiceApplyVO.getReceiverAddress())
                .memo(financeInvoiceApplyVO.getMemo())
                .extendsAttr(invoiceExtendsAttr)
                .applyInvoiceDetailDtoList(canApplyInvoiceList)
                .business_type(InvoiceBusinessType.Product.name())
                .apply_member_id(RequestContext.getCorpLoginId())
                .apply_member_name(RequestContext.getLoginRealName())
                .purchase_organization_id(Utils.createSelecorFormat(transactionOrganiztion.getId(),transactionOrganiztion.getName(),1))
                .purchase_organization_name(transactionOrganiztion.getName())
                .sale_organization_id(transactionOrganiztion.getPlatform_id().get(0))
                .purchase_organization_name(transactionOrganiztion.getPlatform_name())
                .submit(true)
                .build();
        try{
            transactionTemplate.execute(status -> {
                List<String> invoiceIds = invoiceApplyService.createProductInvoice(Collections.singletonList(invoiceApplyForm));
                invoiceApplyService.submit(invoiceIds);
                return null;
            });
            log.info("方法结束:执行类：{},执行方法：customerInvoiceApply（【客户】客户申请开票）,请求人{}" + this.getClass().getName(), RequestContext.getCorpLoginId());
        }catch (IllegalArgumentException exception){
            return GatewayResponse.failed(exception.getMessage());
        }
       return GatewayResponse.success("申请开票成功");
    }
    @Path("saveApplyVoucher")
    @POST
    @Operation(summary = "外部开放接口-客户保存付款凭证（创建销售收款结算单并核销）",description = "",tags = CUSTOM_SERVICE)
    public GatewayResponse saveApplyVoucher(GatewayParam gatewayParam){
        SaleSettleApplyVo saleSettleApplyVo = BeanUtil.mapToBean(gatewayParam.getBody(), SaleSettleApplyVo.class, true);
        log.info("方法开始:执行类：{},执行方法：saveApplyVoucher（客户保存付款凭证）,请求人{}" + this.getClass().getName(), RequestContext.getCorpLoginId());

        //参数验证
        boolean idF = (saleSettleApplyVo.getCustomerOrderIds() == null || saleSettleApplyVo.getCustomerOrderIds().size() == 0)
                && (saleSettleApplyVo.getOrderGoodsIds() == null || saleSettleApplyVo.getOrderGoodsIds().size() == 0);
        if (idF) {
            return GatewayResponse.failed("客户订单号和订单商品号不可同时为空");
        }
        boolean bankF = (saleSettleApplyVo.getBankSerialNumber() == null || "".equals(saleSettleApplyVo.getBankSerialNumber())) && (saleSettleApplyVo.getBankSerialImage() == null || "".equals(saleSettleApplyVo.getBankSerialImage()));
        if (bankF) {
            return GatewayResponse.failed("银行流水号和流水附件不可同时为空");
        }
        transactionTemplate.execute(status -> {
            MemberBankAccountDto memberBankAccountEntity = JdbcTemplateUtils.builder(AppConst.PERSONAL_CENTER,ModelConst.T_MEMBER_BANK_ACCOUNT).selectField("*")
                    .eq("JSON_UNQUOTE(member_id -> '$.id')", PlatformConst.corpLoginId)
                    .eq("default_bank_account", 1)
                    .queryForPOJO(MemberBankAccountDto.class);


            SettleApplyQuery applyQuery = SettleApplyQuery.builder().build();
            if (saleSettleApplyVo.getCustomerOrderIds() != null && saleSettleApplyVo.getCustomerOrderIds().size() > 0) {
                applyQuery.setApply_type(SettleApplyType.ORDER);
                applyQuery.setIds(saleSettleApplyVo.getCustomerOrderIds());
            } else {
                applyQuery.setApply_type(SettleApplyType.ORDER_GOODS);
                applyQuery.setIds(saleSettleApplyVo.getOrderGoodsIds());
            }
            AttachmentFormat attachmentFormat = new AttachmentFormat();
            if(StringUtils.isNotEmpty(saleSettleApplyVo.getBankSerialImage())){
                try {
                    //将文件上传到云枢oss
                    URL url = new URL(saleSettleApplyVo.getBankSerialImage());
                    URLConnection urlConnection = url.openConnection();
                    InputStream inputStream = urlConnection.getInputStream();
                    MultipartFile multipartFile = new MockMultipartFile("", inputStream);
                    SingleResponse<AttachmentRes> response = ftpFeignServiceI.uploadAttachment(multipartFile);
                    attachmentFormat.setId(response.getData().getId());
                    attachmentFormat.setFileSize(response.getData().getFileSize().doubleValue());
                    attachmentFormat.setMimeType(response.getData().getMimeType());
                    attachmentFormat.setName(response.getData().getName());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            SettleInvoice settleInvoice = settleService.viewSettleInvoice(applyQuery);
            List<SettleInvoiceDetail> settleInvoiceDetails = settleInvoice.getSettleInvoiceDetails();
            BigDecimal total = CalculatorUtils.sum(settleInvoiceDetails, SettleService.settleInvoiceDetailWriteOffAmountFun);
            settleInvoiceDetails.forEach(item-> item.setBatch_total(item.getWrite_off_amount()));
            settleInvoice.setApply_member_id(RequestContext.getCorpLoginId())
                    .setApply_member_name(RequestContext.getLoginRealName())
                    .setConfirm_member_id(settleInvoice.getApply_member_id())
                    .setConfirm_member_name(settleInvoice.getApply_member_name())
                    .setConfirm_time(new Date())
                    .setTotal(total)
                    .setType(SettleType.Bank)
                    .setBusiness_type(SettleBusinessType.SaleReceivable)
                    .setSale_bank_account(memberBankAccountEntity.getBank_account())
                    .setSale_bank_info(memberBankAccountEntity.getBank_info())
                    .setSale_bank_no(memberBankAccountEntity.getBank_no().replace(" ", ""))
                    .setSale_phone(memberBankAccountEntity.getPhone())
                    .setBank_serial_number(saleSettleApplyVo.getBankSerialNumber())
                    .setBank_serial_image(Collections.singletonList(attachmentFormat));
            String settleId = settleService.createAdvanceSettle(settleInvoice);
            settleInvoice.setId(settleId);
            settleInvoice.setApply_type(SettleApplyType.Invoice);
            settleService.writeOffSettle(settleInvoice);
            return null;
        });

        return GatewayResponse.success();
    }

    @Path("muyuanListInvoiceByDetail")
    @POST
    @Operation(summary = "外部开放接口-根据客户订单编号获取发票单附件地址",description = "",tags = CUSTOM_SERVICE)
    public GatewayResponse<List<Map<String, Object>>> muyuanListInvoiceByDetail(GatewayParam gatewayParam){
        String orderIds = (String) gatewayParam.getBody().get("orderIds");
        if (StringUtils.isBlank(orderIds) || !RegexUtils.check(RegexUtils.Type.ONLY_NUMBER_OR_EMPTY, orderIds)) {
            return GatewayResponse.failed("参数不能为空或格式有误");
        }
        String sql = "select tia.attachments_json from "+JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_APPLY) + " as tia" +
                " left join " + JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_DETAIL) + " as tid" +
                " on tia.id = tid.invoice_apply_id and tid.deleted = 0" +
                " left join " + JdbcTemplateUtils.getTableName(ModelConst.T_TRADE) + " as trade" +
                " on trade.id = tid.trade_id" +
                " where trade.customer_order_id in (:customerOrderId) and tia.attachments_json is not null";
        List<Map<String, Object>> attachements = JdbcTemplateUtils.queryForList(sql, Maps.newHashMap("customerOrderId", orderIds.split(",")));
        List<Map<String, Object>> result = new ArrayList<>();
        attachements.forEach(item->{
            List<AttachmentFormat> attachmentFormats = JSONUtil.toList(JSONUtil.parseArray(item.get("attachments_json").toString()), AttachmentFormat.class);
            for (AttachmentFormat attachmentFormat : attachmentFormats){
                Map<String, Object> map = new HashMap<>();
                map.put("name", attachmentFormat.getName());
                //TODO 返回的发票地址是否需要跟固买的一样
                map.put("url", "http://120.55.98.117/api/file/download?refId="+attachmentFormat.getId());
                //TODO 产品没有对图片上传时间做记录
                map.put("time", new Date());
                result.add(map);
            }

        });
        return GatewayResponse.success(result.size(),result);
    }

    public GatewayResponse checkParam(FinanceInvoiceApplyVO param) {
        if (param == null) {
            return GatewayResponse.failed("参数不可为空！");
        } else {
            //收货人
            if (param.getReceiverName() == null || param.getReceiverName().isEmpty()) {
                return GatewayResponse.failed("收货人姓名不能为空！");
            }
            //收货地址
            if (param.getReceiverAddress() == null || param.getReceiverAddress().isEmpty()) {
                return GatewayResponse.failed("收货地址不能为空！");
            }
            //收货人电话
            if (param.getReceiverMobile() == null || param.getReceiverMobile().isEmpty()) {
                return GatewayResponse.failed("收货人电话不能为空！");
            }
            //发票类型
            if (param.getInvoiceType() == null) {
                return GatewayResponse.failed("发票类型不能为空！");
            }
            //发票抬头
            if (param.getInvoiceName() == null || param.getInvoiceName().isEmpty()) {
                return GatewayResponse.failed("发票抬头不能为空！");
            }
            //税号
            if (param.getInvoiceTaxNumber() == null || param.getInvoiceTaxNumber().isEmpty()) {
                return GatewayResponse.failed("税号不能为空！");
            }
            //开票地址
            if (param.getInvoiceAddress() == null || param.getInvoiceAddress().isEmpty()) {
                return GatewayResponse.failed("开票地址不能为空！");
            }
            //开票电话
            if (param.getInvoicePhone() == null || param.getInvoicePhone().isEmpty()) {
                return GatewayResponse.failed("开票电话不能为空！");
            }
            //开户行
            if (param.getInvoiceBank() == null || param.getInvoiceBank().isEmpty()) {
                return GatewayResponse.failed("开户行不能为空！");
            }
            //发票账号
            if (param.getInvoiceAccount() == null || param.getInvoiceAccount().isEmpty()) {
                return GatewayResponse.failed("发票账号不能为空！");
            }
            //客户订单号和订单商品编号
            boolean idIsNull = (param.getCustomerOrderIds() == null || param.getCustomerOrderIds().isEmpty()) &&
                    (param.getOrderGoodsIds() == null || param.getOrderGoodsIds().isEmpty());
            if (idIsNull) {
                return GatewayResponse.failed("客户订单号和订单商品号不可同时为空，请选填一项！");
            }
            boolean idNotNull = (param.getCustomerOrderIds() != null && !param.getCustomerOrderIds().isEmpty()) &&
                    (param.getOrderGoodsIds() != null && !param.getOrderGoodsIds().isEmpty());
            if (idNotNull) {
                return GatewayResponse.failed("客户订单号和订单商品号不可同时存在，请选填一项！");
            }

        }
        log.info("方法结束:执行类：{},执行方法：pageCustomerApply（显示客户的付款申请单）,请求人{}" + this.getClass().getName(), RequestContext.getCorpLoginId());
        return GatewayResponse.success();
    }
}
